package com.tt.utils;

import java.util.Arrays;
import java.util.Objects;

/**
 * 三重校验位工具类
 * 功能：基于原始数字最后3位作为种子，在伪随机位置插入三个不同校验位
 * 特点：
 * 1. 无需额外存储位置信息
 * 2. 校验位位置由数字内容决定，可重复计算
 * 3. 支持任意长度数字（至少3位）
 */
public class TripleCheckDigitUtil {

    /**
     * 为原始数字添加三重校验位
     * @param originalNumber 原始数字（至少3位）
     * @return 带有三重校验位的数字字符串
     * @throws IllegalArgumentException 如果输入无效
     */
    public static String addCheckDigits(String originalNumber) {
        validateInput(originalNumber);

        // 计算三种校验位
        final int luhnDigit = calculateLuhnCheckDigit(originalNumber);
        final int simpleDigit = calculateSimpleCheckDigit(originalNumber);
        final int mod97Digit = calculateMod97CheckDigit(originalNumber);

        // 获取最后3位作为位置种子（这部分保持不变）
        final String lastThree = originalNumber.substring(originalNumber.length() - 3);

        // 计算插入位置（原始数字长度，不包括最后3位）
        final int[] positions = calculateInsertPositions(originalNumber.length() - 3, lastThree);

        // 按顺序插入校验位（从小到大）
        return insertDigitsAtPositions(
                originalNumber,
                new int[]{luhnDigit, simpleDigit, mod97Digit},
                positions
        );
    }

    /**
     * 验证带校验位的数字
     * @param numberWithChecks 带校验位的数字
     * @return 验证通过返回true
     */
    public static boolean validateCheckDigits(String numberWithChecks) {
        try {
            validateInput(numberWithChecks);
            if (numberWithChecks.length() < 6) {
                return false;
            }

            // 原始数字长度 = 当前长度 - 3个校验位
            int originalLength = numberWithChecks.length() - 3;

            // 最后3位必须是原始数字的最后3位
            String originalLastThree = numberWithChecks.substring(originalLength);

            // 计算插入位置（原始数字坐标系）
            int[] originalPositions = calculateInsertPositions(originalLength - 3, originalLastThree);
            int[] actualPositions = adjustPositionsAfterInsertion(originalPositions);

            // 提取校验位
            int[] extractedDigits = new int[3];
            for (int i = 0; i < 3; i++) {
                extractedDigits[i] = Character.getNumericValue(numberWithChecks.charAt(actualPositions[i]));
            }

            // 重建原始数字（移除校验位）
            String originalNumber = rebuildOriginalNumber(numberWithChecks, actualPositions);

            // 验证校验位
            return extractedDigits[0] == calculateLuhnCheckDigit(originalNumber)
                    && extractedDigits[1] == calculateSimpleCheckDigit(originalNumber)
                    && extractedDigits[2] == calculateMod97CheckDigit(originalNumber);
        } catch (Exception e) {
            return false;
        }
    }

    // ========== 私有方法 ==========

    private static void validateInput(String number) {
        Objects.requireNonNull(number, "输入不能为null");
        if (!number.matches("\\d+")) {
            throw new IllegalArgumentException("输入必须为纯数字");
        }
        if (number.length() < 3) {
            throw new IllegalArgumentException("数字长度至少需要3位");
        }
    }

    private static int[] calculateInsertPositions(int length, String lastThree) {
        final int seed1 = Character.getNumericValue(lastThree.charAt(0));
        final int seed2 = Character.getNumericValue(lastThree.charAt(1));
        final int seed3 = Character.getNumericValue(lastThree.charAt(2));

        // 计算初始位置（确保在0~length-1范围内）
        int pos1 = (seed1 * length) % length;
        int pos2 = (pos1 + seed2 + 1) % length;
        int pos3 = (pos2 + seed3 + 1) % length;

        // 确保三个位置不同且不在最后3位
        while (pos2 == pos1 || pos2 >= length){
            pos2 = (pos2 + 1) % length;
        }
        while (pos3 == pos1 || pos3 == pos2 || pos3 >= length){
            pos3 = (pos3 + 1) % length;
        }

        return sortThreePositions(pos1, pos2, pos3);
    }

    private static int[] sortThreePositions(int a, int b, int c) {
        if (a > b) { int temp = a; a = b; b = temp; }
        if (b > c) { int temp = b; b = c; c = temp; }
        if (a > b) { int temp = a; a = b; b = temp; }
        return new int[]{a, b, c};
    }


    private static String insertDigitsAtPositions(
            String original,
            int[] digits,
            int[] positions
    ) {
        final StringBuilder sb = new StringBuilder(original);

        // 插入前先确保最后3位保持不变
        String lastThree = original.substring(original.length() - 3);
        sb.setLength(original.length() - 3);

        // 按顺序插入校验位
        sb.insert(positions[0], digits[0]);
        sb.insert(positions[1] + 1, digits[1]);
        sb.insert(positions[2] + 2, digits[2]);

        // 恢复最后3位
        sb.append(lastThree);
        return sb.toString();
    }

    private static int[] adjustPositionsAfterInsertion(int[] originalPositions) {
        final int[] adjusted = new int[3];
        adjusted[0] = originalPositions[0]; // 第一个插入位置不变
        adjusted[1] = originalPositions[1] + 1; // 第二个位置+1
        adjusted[2] = originalPositions[2] + 2; // 第三个位置+2
        return adjusted;
    }

    private static String rebuildOriginalNumber(String numberWithChecks, int[] checkDigitPositions) {
        // 先对位置排序
        int[] sortedPositions = checkDigitPositions.clone();
        Arrays.sort(sortedPositions);

        StringBuilder sb = new StringBuilder(numberWithChecks);
        // 从高到低删除校验位，避免位置变化影响
        sb.deleteCharAt(sortedPositions[2]);
        sb.deleteCharAt(sortedPositions[1]);
        sb.deleteCharAt(sortedPositions[0]);
        return sb.toString();
    }

    // ========== 校验位计算方法 ==========

    private static int calculateLuhnCheckDigit(String number) {
        int sum = 0;
        boolean alternate = true;
        for (int i = number.length() - 1; i >= 0; i--) {
            int digit = Character.getNumericValue(number.charAt(i));
            if (alternate) {
                digit *= 2;
                if (digit > 9){
                    digit = (digit % 10) + 1;
                }
            }
            sum += digit;
            alternate = !alternate;
        }
        return (10 - (sum % 10)) % 10;
    }

    private static int calculateSimpleCheckDigit(String number) {
        int sum = 0;
        for (char c : number.toCharArray()) {
            sum += Character.getNumericValue(c);
        }
        return sum % 10;
    }

    private static int calculateMod97CheckDigit(String number) {
        int remainder = 0;
        for (char c : number.toCharArray()) {
            int digit = Character.getNumericValue(c);
            remainder = (remainder * 10 + digit) % 97;
        }
        return remainder % 10;
    }

    // ========== 测试用例 ==========

    public static void main(String[] args) {
//        testCase("11056729484624901"); // 标准12位数字
//        testCase("123");          // 最小3位数字
//        testCase("12345678901234567890"); // 长数字
//        testCase("139");
        String withChecks = addCheckDigits("91109961186287820985");
        System.out.println("添加校验位后: " + withChecks);
        System.out.println(validateCheckDigits(withChecks));

    }

    private static void testCase(String original) {
        System.out.println("\n测试用例: " + original);

        String withChecks = addCheckDigits(original);
        System.out.println("添加校验位后: " + withChecks);

        boolean isValid = validateCheckDigits(withChecks);
        System.out.println("验证结果: " + isValid);

        // 篡改测试
        if (withChecks.length() > 5) {
            String tampered = withChecks.substring(0, 3) + "" + withChecks.substring(6);
            System.out.println("篡改后验证: " + validateCheckDigits(tampered));
        }
    }
}
